#ifndef _CONFIG_H_
#define _CONFIG_H_

#include <vector>
#include <array>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <chrono>
#include <ctime>
#include <cstdlib>
#include <cmath>

using Real = double;
using RealVect = std::vector<Real>;
using RealBox = std::vector<RealVect>; 
#define PGET(_value_,_var_) _var_=_value_[#_var_]
template <class T>
constexpr T Max(const T &a, const T &b)
{
    return a > b ? a : b;
};

constexpr int fac(const int x)
{
    int f = 1;
    if (x == 0 || x == 1)
        return f;
    else
        return fac(x - 1) * x;
};


//==================================================================
// Formatting

template <class T>
inline
std::string oneOver(const T &denom) {
  std::ostringstream oss;
  oss << "1/" << denom;
  return oss.str();
}

template <std::size_t numOfNorms>
inline
void printConvergenceTable(std::vector<int> gridSize,
                           const std::vector<std::array<Real, numOfNorms>> &errnorm)
{
  const int numCompHier = errnorm.size();
  const int w = 10;
  const char *ntHeader[] = {"$L^\\infty$", "$L^1$", "$L^2$"};
  std::cout << "\n" << std::setw(w) << "$h$";
  for(int n = numCompHier-1; n >= 0; --n) {
    if (n > 0)
      std::cout << " & " << std::setw(w) << oneOver(gridSize[n]) << " & " << std::setw(w) << "rate";
    else
      std::cout << " & " << std::setw(w) << oneOver(gridSize[n]) << " \\\\\n";
  }
  std::cout.precision(2);
  for(std::size_t p=0; p<numOfNorms; ++p) {
    std::cout << std::setw(w) << ntHeader[p%3];
    for(int n = numCompHier-1; n >= 0; --n) {
      std::cout << " & " << std::scientific << std::setw(w) << errnorm[n][p];
      if(n != 0)
        std::cout << " & " << std::fixed << std::setw(w) << log(errnorm[n-1][p] / errnorm[n][p]) / log(1.0 * gridSize[n] / gridSize[n-1]);
    }
    std::cout << " \\\\" << std::endl;
  }
}
#else
//do nothing
#endif
